﻿using System;
using System.IO;
using System.Text;
using System.Collections.Generic;

using Poi.NET.util;

namespace Poi.NET.hpsf
{
    /**
     *  <p>Represents a class ID (16 bytes). Unlike other little-endian
     *  type the {@link ClassID} is not just 16 bytes stored in the wrong
     *  order. Instead, it is a double word (4 bytes) followed by two
     *  words (2 bytes each) followed by 8 bytes.</p>
     *
     * @author Rainer Klute <a
     * href="mailto:klute@rainer-klute.de">&lt;klute@rainer-klute.de&gt;</a>
     */
    public class ClassID
    {

        /**
         * <p>The bytes making out the class ID in correct order,
         * i.e. big-endian.</p>
         */
        protected byte[] bytes;


        /**
         *  <p>Creates a {@link ClassID} and reads its value from a byte
         *  array.</p>
         *
         * @param src The byte array to read from.
         * @param offset The offset of the first byte to read.
         */
        public ClassID(byte[] src, int offset)
        {
            read(src, offset);
        }


        /**
         *  <p>Creates a {@link ClassID} and initializes its value with
         *  0x00 bytes.</p>
         */
        public ClassID()
        {
            bytes = new byte[LENGTH];
            for (int i = 0; i < LENGTH; i++)
                bytes[i] = 0x00;
        }


        /** <p>The number of bytes occupied by this object in the byte
         * stream.</p> */
        public static readonly int LENGTH = 16;

        /**
         * @return The number of bytes occupied by this object in the byte
         * stream.
         */
        public int length()
        {
            return LENGTH;
        }


        /**
         * <p>Gets the bytes making out the class ID. They are returned in
         * correct order, i.e. big-endian.</p>
         *
         * @return the bytes making out the class ID.
         */
        public byte[] getBytes()
        {
            return bytes;
        }


        /**
         * <p>Sets the bytes making out the class ID.</p>
         *
         * @param bytes The bytes making out the class ID in big-endian format. They
         * are copied without their order being changed.
         */
        public void setBytes(byte[] bytes)
        {
            for (int i = 0; i < this.bytes.Length; i++)
                this.bytes[i] = bytes[i];
        }


        /**
         * <p>Reads the class ID's value from a byte array by turning
         * little-endian into big-endian.</p>
         *
         * @param src The byte array to read from
         *
         * @param offset The offset within the <var>src</var> byte array
         *
         * @return A byte array containing the class ID.
         */
        public byte[] read(byte[] src, int offset)
        {
            bytes = new byte[16];

            /* Read double word. */
            bytes[0] = src[3 + offset];
            bytes[1] = src[2 + offset];
            bytes[2] = src[1 + offset];
            bytes[3] = src[0 + offset];

            /* Read first word. */
            bytes[4] = src[5 + offset];
            bytes[5] = src[4 + offset];

            /* Read second word. */
            bytes[6] = src[7 + offset];
            bytes[7] = src[6 + offset];

            /* Read 8 bytes. */
            for (int i = 8; i < 16; i++)
                bytes[i] = src[i + offset];

            return bytes;
        }


        /**
         * <p>Writes the class ID to a byte array in the
         * little-endian format.</p>
         *
         * @param dst The byte array to write to.
         *
         * @param offset The offset within the <var>dst</var> byte array.
         *
         * @exception ArrayStoreException if there is not enough room for the class
         * ID 16 bytes in the byte array after the <var>offset</var> position.
         */
        public void write(byte[] dst, int offset)
        {
            /* Check array size: */
            if (dst.Length < 16)
                throw new Exception
                    ("Destination byte[] must have room for at least 16 bytes, " +
                     "but has a length of only " + dst.Length + ".");
            /* Write double word. */
            dst[0 + offset] = bytes[3];
            dst[1 + offset] = bytes[2];
            dst[2 + offset] = bytes[1];
            dst[3 + offset] = bytes[0];

            /* Write first word. */
            dst[4 + offset] = bytes[5];
            dst[5 + offset] = bytes[4];

            /* Write second word. */
            dst[6 + offset] = bytes[7];
            dst[7 + offset] = bytes[6];

            /* Write 8 bytes. */
            for (int i = 8; i < 16; i++)
                dst[i + offset] = bytes[i];
        }


        /**
         * <p>Checks whether this <code>ClassID</code> is equal to another
         * object.</p>
         *
         * @param o the object to compare this <code>PropertySet</code> with
         * @return <code>true</code> if the objects are equal, else
         * <code>false</code>.</p>
         */
        public boolean equals(Object o)
    {
        if (o == null || !(o is ClassID))
            return false;
        ClassID cid = (ClassID) o;
        if (bytes.Length != cid.bytes.Length)
            return false;
        for (int i = 0; i < bytes.Length; i++)
            if (bytes[i] != cid.bytes[i])
                return false;
        return true;
    }


        /**
         * @see Object#hashCode()
         */
        public int hashCode()
        {
            return bytes.GetHashCode();
        }


        /**
         * <p>Returns a human-readable representation of the Class ID in standard 
         * format <code>"{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"</code>.</p>
         * 
         * @return String representation of the Class ID represented by this object.
         */
        public String toString()
        {
            StringBuilder sbClassId = new StringBuilder(38);
            sbClassId.Append('{');
            for (int i = 0; i < 16; i++)
            {
                sbClassId.Append(HexDump.toHex(bytes[i]));
                if (i == 3 || i == 5 || i == 7 || i == 9)
                    sbClassId.Append('-');
            }
            sbClassId.Append('}');
            return sbClassId.ToString();
        }

    }
}